/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "avmplus.h"

namespace avmplus
{
    XMLListObject::XMLListObject(XMLListClass *type, Atom tObject, const Multiname* tProperty)
        : ScriptObject(type->ivtable(), type->prototypePtr())
#ifdef DEBUGGER
        , m_created(false)
        , m_flushBeforeConstructionComplete(false)
#endif
        , m_targetObject(tObject)
        , m_appended(false)
        , m_children(type->gc(), 0)
    {
        if (tProperty)
            m_targetProperty = *tProperty;
        // unnecessary: the default ctor for HeapMultiname (and Multiname) init to an anyName
        //else // should be equivalent to a null m_targetProperty
        //  m_targetProperty.setAnyName();
#ifdef DEBUGGER
        m_created = true;
        if (m_flushBeforeConstructionComplete) {
            registerDependentObjects(getSampler());
            getSampler()->recordObjectReallocation(this);
        }
#endif
    }

    //////////////////////////////////////////////////////////////////////
    // E4X Section 9.2.1 below (internal methods)
    //////////////////////////////////////////////////////////////////////

    // this = argv[0] (ignored)
    // arg1 = argv[1]
    // argN = argv[argc]
    Atom XMLListObject::callProperty(const Multiname* multiname, int argc, Atom* argv)
    {
        // sec 11.2.2.1 CallMethod(r,args)

        Atom f = getDelegate()->getMultinameProperty(multiname);
        if (f == undefinedAtom)
        {
            f = getMultinameProperty(multiname);
            // If our method returned is a 0 element XMLList, it means that we did not
            // find a matching property for this method name.  In this case, if our XMLList
            // has one child, we get the child and callproperty on the child object.
            // This allows node elements to be treated as simple strings even if they
            // are XML or XMLList objects.  See 11.2.2.1 in the E4X spec for CallMethod.
            if (AvmCore::isXMLList(f) &&
                !AvmCore::atomToXMLList(f)->_length() &&
                (this->_length() == 1))
            {
                XMLObject *x0 = _getAt(0);
                return x0->callProperty (multiname, argc, argv);
            }
        }
        argv[0] = atom(); // replace receiver
        return avmplus::op_call(toplevel(), f, argc, argv);
    }

    Atom XMLListObject::getMultinameProperty(const Multiname* m) const
    {
        AvmCore *core = this->core();
        Toplevel* toplevel = this->toplevel();

        // step 1 - We have an integer argument - direct child lookup
        if (!m->isAnyName() && !m->isAttr())
        {
            uint32_t index;
            if (!m->isAnyName() && m->getName()->parseIndex(index))
            {
                return getUintProperty (index);
            }
        }

        // step 2
        //QNameObject* qname = new (core->GetGC()) QNameObject(toplevel->qnameClass(), *m);
        XMLListObject *l = XMLListObject::create(core->GetGC(), toplevel->xmlListClass(), this->atom(), m);

        // step 3
        for (uint32_t i = 0; i < _length(); i++)
        {
            E4XNode* node = _getNodeAt(i);
            if (node->getClass() == E4XNode::kElement)
            {
                // step 3ai
                XMLObject* xml = _getAt(i);
                Atom gq = xml->getMultinameProperty(m);
                if (AvmCore::atomToXML(gq))
                {
                    XMLObject *x = AvmCore::atomToXMLObject(gq);
                    if (x && x->_length())
                        l->_append (gq);
                }
                else
                {
                    XMLListObject *xl = AvmCore::atomToXMLList(gq);
                    if (xl && xl->_length())
                    {
                        l->_append (gq);
                    }
                }
            }
        }

        return l->atom();
    }

    void XMLListObject::setMultinameProperty(const Multiname* m, Atom V)
    {
        Toplevel *toplevel = this->toplevel();

        uint32_t i;
        if (!m->isAnyName() && !m->isAttr() && m->getName()->parseIndex(i))
        {
            setUintProperty (i, V);
            return;
        }
        // step 3
        else if (_length() <= 1)
        {
            //step3a
            if (_length() == 0)
            {
                Atom r = this->_resolveValue();
                if (AvmCore::isNull(r))
                    return;

                if (AvmCore::isXML(r) && (AvmCore::atomToXML(r)->_length() != 1))
                    return;

                if (AvmCore::isXMLList(r) && (AvmCore::atomToXMLList(r)->_length() != 1))
                    return;

                this->_append (r);
            }

            XMLObject *x = _getAt (0);
            x->setMultinameProperty(m, V);
        }
        else if (_length() > 1)
        {
            toplevel->throwTypeError(kXMLAssigmentOneItemLists);
        }

#ifdef _DEBUG
        // The only thing allowed in XMLLists is XML objects
        for (uint32_t z = 0; z < _length(); z++)
        {
            AvmAssert(AvmCore::isXML(m_children.get(z)));
        }
#endif

        return;
    }

    bool XMLListObject::deleteMultinameProperty(const Multiname* m)
    {
        if (!m->isAnyName() && !m->isAttr())
        {
            Stringp name = m->getName();
            uint32_t index;
            if (name->parseIndex(index))
            {
                return this->delUintProperty (index);
            }
        }

        for (uint32_t i = 0; i < numChildren(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *xm = _getAt(i);
                xm->deleteMultinameProperty(m);
            }
        }

        return true;
    }

    Atom XMLListObject::getDescendants(const Multiname* m) const
    {
        AvmCore *core = this->core();

        XMLListObject *l = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass());
        for (uint32_t i = 0; i < numChildren(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *xm = _getAt(i);
                Atom dq = xm->getDescendants (m);
                XMLListObject *dqx = AvmCore::atomToXMLList(dq);
                if (dqx && dqx->_length())
                {
                    l->_append (dq);
                }
            }
        }

        return l->atom();
    }

    // E4X 9.2.1.1, page 21
    Atom XMLListObject::getAtomProperty(Atom P) const
    {
        Multiname m;
        toplevel()->ToXMLName (P, m);
        return getMultinameProperty(&m);
    }

    // E4X 9.2.1.2, pg 21 - [[PUT]]
    void XMLListObject::setAtomProperty(Atom P, Atom V)
    {
        Multiname m;
        toplevel()->ToXMLName (P, m);
        setMultinameProperty(&m, V);
    }

    Atom XMLListObject::getUintProperty(uint32_t index) const
    {
        if (index < _length())
        {
            return _getAt(index)->atom();
        }
        else
        {
            return undefinedAtom;
        }
    }

    void XMLListObject::setUintProperty(uint32_t i, Atom V)
    {
        AvmCore* core = this->core();
        Toplevel* toplevel = this->toplevel();

        Atom r = nullStringAtom;
        fixTargetObject();
        // step 2a
        if (!AvmCore::isNull(this->m_targetObject))
        {
            if (AvmCore::isXML(m_targetObject))
            {
                r = AvmCore::atomToXMLObject(m_targetObject)->_resolveValue();
            }
            else if (AvmCore::isXMLList(m_targetObject))
            {
                r = AvmCore::atomToXMLList(m_targetObject)->_resolveValue();
            }

            if (AvmCore::isNull(r))
                return;
        }

        // step 2c
        if (i >= _length())
        {
            // step 2 c i
            if (AvmCore::isXMLList(r))
            {
                XMLListObject *xl = AvmCore::atomToXMLList(r);
                if (xl->_length() != 1)
                    return;
                else
                    r = xl->_getAt(0)->atom();

                // r should now be an XMLObject atom from this point forward
            }

            // step 2cii - newer spec
            XMLObject *rx = AvmCore::atomToXMLObject(r);
            if (rx && rx->getClass() != E4XNode::kElement)
                return;

            E4XNode *y = 0;
            // step 2 c iii
            if (m_targetProperty.isAttr())
            {
                Atom attributesExist = rx->getMultinameProperty(m_targetProperty);
                if (AvmCore::isXMLList(attributesExist) && (AvmCore::atomToXMLList(attributesExist)->_length() > 0))
                    return;

                y = AttributeE4XNode::create(core->GetGC(), AvmCore::atomToXML(r), 0);
                y->setQName (core, m_targetProperty);

                // warning: it looks like this is sent prior to the attribute being added.
                rx->nonChildChanges(core->kattributeAdded, this->m_targetProperty.getName()->atom());
            }
            // if targetProperty is null or targetProperty.localName = "*"
            else if (m_targetProperty.isAnyName() ||
                // Added because of bug 145184 - appendChild with a text node not working right
                // Also handle attribute nodes
                ((AvmCore::isXML(V) && AvmCore::atomToXML(V)->getClass() == E4XNode::kAttribute)) ||
                ((AvmCore::isXML(V) && AvmCore::atomToXML(V)->getClass() == E4XNode::kText)))
            {
                y = TextE4XNode::create(core->GetGC(), AvmCore::atomToXML(r), 0);
            }
            else
            {
                y = ElementE4XNode::create(core->GetGC(), AvmCore::atomToXML(r));
                y->setQName (core, this->m_targetProperty);
            }

            i = _length();
            // step 2.c.viii
            if (y->getClass() != E4XNode::kAttribute)
            {
                AvmAssert(y->getParent() == (rx ? rx->getNode() : 0));
                if (y->getParent() != NULL)
                {
                    // y->m_parent = r = XMLObject *
                    E4XNode *parent = y->getParent();
                    AvmAssert(parent != 0);
                    uint32_t j = 0;
                    if (i > 0)
                    {
                        // step 2 c vii 1 ii
                        // spec says "y.[[parent]][j] is not the same object as x[i-1]" and I'm
                        // assuming that means pointer equality and not "equals" comparison
                        while ((j < (parent->_length() - 1)) && parent->_getAt (j) != _getNodeAt(i - 1))
                        {
                            j++;
                        }

                        j = j + 1; // slightly different than spec but our insert call does not have a + 1 on it
                    }
                    else
                    {
                        j = parent->_length(); // slightly different than spec but our insert call does not have a + 1 on it
                    }
                    // These two lines are equivalent to _insert which takes
                    // an Atom since we know that y is an XMLObject/E4XNode
                    y->setParent (parent);
                    parent->insertChild (j, y);

                }
                if (AvmCore::isXML(V))
                {
                    Multiname mV;
                    if (AvmCore::atomToXML(V)->getQName(&mV, core->findPublicNamespace()))
                        y->setQName (core, &mV);

                }
                else if (AvmCore::isXMLList(V))
                {
                    //ERRATA : 9.2.1.2 step 2(c)(vii)(3) what is V.[[PropertyName]]? s.b. [[TargetProperty]]
                    if (!AvmCore::atomToXMLList(V)->m_targetProperty.isAnyName())
                        y->setQName (core, AvmCore::atomToXMLList(V)->m_targetProperty);
                }
            }

            this->_appendNode (y);
        }

        // step 2d
        if (AvmCore::atomToXMLList(V))
        {
            XMLListObject *src = AvmCore::atomToXMLList(V);
            if ((src->_length() == 1) && src->_getNodeAt(0)->getClass() & (E4XNode::kText | E4XNode::kAttribute))
            {
                V = core->string(V)->atom();
            }
        }
        else if (AvmCore::atomToXML(V))
        {
            E4XNode *v = AvmCore::atomToXML(V);
            if (v->getClass() & (E4XNode::kText | E4XNode::kAttribute))
            {
                // This string is converted into a XML object below in step 2(g)(iii)
                V = core->string(V)->atom();
            }
        }
        else
        {
            V = core->string(V)->atom();
        }

        // step 2e
        E4XNode *xi = _getNodeAt(i);
        if (xi->getClass() == E4XNode::kAttribute)
        {
            XMLObject* xio = _getAt(i);
            XMLObject *parent = xio->getParent();
            Multiname mxi;
            xio->getQName (&mxi);
            parent->setMultinameProperty(&mxi, V);
            Atom attr = parent->getMultinameProperty(&mxi);
            XMLListObject *attrx = AvmCore::atomToXMLList(attr);
            // x[i] = attr[0];
            m_children.set(i, attrx->m_children.get(0));
        }
        // step 2f
        else if (AvmCore::isXMLList(V))
        {
            // create a shallow copy c of V
            XMLListObject *src = AvmCore::atomToXMLList(V);
            XMLListObject *c = XMLListObject::create(core->GetGC(), toplevel->xmlListClass());

            c->m_children.ensureCapacity(src->numChildren());
            for (uint32_t i2 = 0; i2 < src->numChildren(); i2++)
                c->m_children.add(src->m_children.get(i2));

            E4XNode *parent = _getNodeAt(i)->getParent();
            // step 2 f iii
            if (parent)
            {
                uint32_t q = 0;
                while (q < parent->numChildren())
                {
                    if (parent->_getAt(q) == _getNodeAt(i))
                    {
                        parent->_replace (core, toplevel, q, c->atom());
                        for (uint32_t j = 0; j < c->_length(); j++)
                        {
                            c->m_children.set(j, AvmCore::genericObjectToAtom(parent->_getAt(q + j)));
                        }

                        break;
                    }

                    q++;
                }
            }

            // resize our buffer to ([[Length]] + c.[[Length]]) (2 f vi)

            bool notify = (parent && XMLObject::notifyNeeded(parent));
            XMLObject* prior =  (notify) ? _getAt(i) : 0;
            XMLObject* target = (notify) ? XMLObject::create(core->GetGC(), toplevel->xmlClass(), parent) : 0;

            m_children.removeAt(i);
            for (uint32_t i2 = 0; i2 < c->numChildren(); i2++)
            {
                m_children.insert(i + i2, c->m_children.get(i2));

                E4XNode* node = c->_getNodeAt(i2);
                if (notify && (parent == node->getParent()))
                {
                    XMLObject* obj = c->_getAt(i2);
                    if (i2 == 0)
                    {
                        // @todo; is this condition ever true?
                        if (node != prior->getNode())
                            target->childChanges(core->knodeChanged, obj->atom(), prior->getNode());
                    }
                    else
                    {
                        target->childChanges(core->knodeAdded, obj->atom());
                    }
                }
            }
        }
        // step 2g
        // !!@ always going into this step supports how Rhino acts with setting XMLList props
        //var y = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
        //y[0] = "five";
        //print(y);
        // <alpha>one</alpha> which is an element node is converted to a text node with "five" as it's label
        // !!@ but in this case, the node's contents are replaced and not the node itself...
        // o = <o><i id="1">A</i><i id="2">B</i><i id="3">C</i><i id="4">D</i></o>;
        // o.i[1] = "BB";
        // print(o);
        else if (AvmCore::isXML(V) || xi->getClass() & (E4XNode::kText | E4XNode::kCDATA | E4XNode::kComment | E4XNode::kProcessingInstruction))
        {
            E4XNode *parent = _getNodeAt(i)->getParent();
            if (parent)
            {
                // search our parent for a match between a child and x[i]
                uint32_t q = 0;
                while (q < parent->numChildren())
                {
                    if (parent->_getAt(q) == _getNodeAt(i))
                    {
                        parent->_replace (core, toplevel, q, V);
                        XMLObject *xo = XMLObject::create(core->GetGC(), toplevel->xmlClass(), parent->_getAt(q));
                        V = xo->atom();

                        if (XMLObject::notifyNeeded(parent))
                        {
                            XMLObject *po = XMLObject::create(core->GetGC(), toplevel->xmlClass(), parent);
                            po->childChanges(core->knodeAdded, xo->atom());
                        }
                        break;
                    }

                    q++;
                }
            }

            // From E4X errrata
            //9.2.1.2 step 2(g)(iii) erratum: _V_ may not be of type XML, but all index-named
            //      properties _x[i]_ in an XMLList _x_ must be of type XML, according to
            //      9.2.1.1 Overview and other places in the spec.

            //      Thanks to 2(d), we know _V_ (*vp here) is either a string or an
            //      XML/XMLList object.  If _V_ is a string, call ToXML on it to satisfy
            //      the constraint before setting _x[i] = V_.

            if (AvmCore::isXML(V))
                m_children.set(i, V);
            else
                m_children.set(i, xmlClass()->ToXML (V));
        }
        // step 2h
        else
        {
            _getAt(i)->setStringProperty(core->kAsterisk, V);
        }
    }

    bool XMLListObject::delUintProperty(uint32_t index)
    {
        if (index >= _length())
        {
            return true;
        }
        else
        {
            XMLObject *xi = _getAt(index);
            XMLObject *px = xi->getParent();
            if (px != NULL)
            {
                if (xi->getClass() == E4XNode::kAttribute)
                {
                    Multiname mx;
                    xi->getQName (&mx);
                    px->deleteMultinameProperty(&mx);
                }
                else
                {
                    // let q be the property of parent, where parent[q] is the same object as x[i].
                    int q = xi->childIndex ();

                    E4XNode *x = px->getNode()->_getAt(q);
                    px->getNode()->_deleteByIndex (q);

                    if (XMLObject::notifyNeeded(px->getNode()) && x->getClass() == E4XNode::kElement)
                    {
                        AvmCore *core = this->core();
                        XMLObject *r = XMLObject::create(core->GetGC(), xmlClass(), x);
                        px->childChanges(core->knodeRemoved, r->atom());
                    }
                }
            }

			// https://watsonexp.corp.adobe.com/#bug=3993626
			// The problem is with the notifications for removal being sent in childChanges() 
			// before the call to removeAt() and the event handler for the notifications 
			// deleting the children. But since changing the order might break exisiting
			// content, placing a range check for the index before the call to removeAt()

            if (index < _length())
			{
				// delete index from this list
				m_children.removeAt(index);
			}
            return true;
        }
    }

    // E4X 9.2.1.3, pg 23
    bool XMLListObject::deleteAtomProperty(Atom P)
    {
        Multiname m;
        toplevel()->ToXMLName (P, m);
        return deleteMultinameProperty(&m);
    }

    bool XMLListObject::hasUintProperty(uint32_t index) const
    {
        return (index < _length());
    }

    bool XMLListObject::hasMultinameProperty(const Multiname* m) const
    {
        if (!m->isAnyName() && !m->isAttr())
        {
            Stringp name = m->getName();
            uint32_t index;
            if (name->parseIndex(index))
            {
                return (index < _length());
            }
        }

        for (uint32_t i = 0; i < numChildren(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *xm = _getAt(i);
                if (xm->hasMultinameProperty(m))
                    return true;
            }
        }

        return false;
    }

    // E4X 9.2.1.5, pg 24
    bool XMLListObject::hasAtomProperty(Atom P) const
    {
        Multiname m;
        toplevel()->ToXMLName (P, m);
        return hasMultinameProperty(&m);
    }

    /*
    This code is extracted from the former _append(E4XNode*) method.
    It is unclear why this code is required, but I left it in to not
    break any existing code. It does not make sense to execute the
    code for every call to _appendNode(). That method now sets m_appended,
    and code that accesses m_targetObject and/or m_targetProperty needs
    to call fixTargetObject() before.
    */
    void XMLListObject::fixTargetObject() const
    {
        if (m_appended && numChildren() > 0)
        {
            AvmCore *core = this->core();
            E4XNode* v = _getNodeAt(numChildren()-1);

            if (v->getParent())
            {
                XMLObject* target = AvmCore::atomToXMLObject(m_targetObject);
                if (target && target->getNode() != v->getParent())
                {
                    // create only a new target object if the old one does not match
                    XMLObject *p = XMLObject::create(core->GetGC(), toplevel()->xmlClass(), v->getParent());
                    setTargetObject(p->atom());
                }
            }
            else
                setTargetObject(nullObjectAtom);
            if (v->getClass() != E4XNode::kProcessingInstruction)
            {
                Multiname m;
                if (v->getQName (&m, core->findPublicNamespace()))
                    this->m_targetProperty = m;
            }
            m_appended = false;
        }
    }

    void XMLListObject::_appendNode(E4XNode *v)
    {
        m_children.add(AvmCore::genericObjectToAtom(v));
        m_appended = true;
    }

    // E4X 9.2.1.6, pg 24
    void XMLListObject::_append(Atom V)
    {
        // !!@ what the docs say
        // Atom children = this->get ("*");
        // children->put (children->length(), child)
        // return x;

        XMLListObject *v = AvmCore::atomToXMLList(V);
        if (v)
        {
            setTargetObject(v->m_targetObject);
            this->m_targetProperty = v->m_targetProperty;
            m_appended = false;

            if (v->_length())
            {
                m_children.ensureCapacity(m_children.length() + v->_length());
                for (uint32_t j = 0; j < v->_length(); j++)
                {
                    m_children.add(v->m_children.get(j));
                }
            }
        }
        else
        {
            XMLObject* v = AvmCore::atomToXMLObject(V);
            if (v)
            {
                m_appended = true;
                m_children.add(V);
            }
        }
    }

    // E4X 9.2.1.7, pg 25
    XMLListObject *XMLListObject::_deepCopy () const
    {
        AvmCore *core = this->core();

        fixTargetObject();
        XMLListObject *l = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), m_targetObject, m_targetProperty);

        l->m_children.ensureCapacity(numChildren());
        for (uint32_t i = 0; i < numChildren(); i++)
        {
            l->m_children.add(_getAt(i)->_deepCopy()->atom());
        }

        return l;
    }

    // E4X 9.2.1.8, pg 25
    XMLListObject *XMLListObject::AS3_descendants (Atom P)
    {
        Multiname m;
        toplevel()->ToXMLName (P, m);
        return AvmCore::atomToXMLList (getDescendants (&m));
    }

    // E4X 9.2.1.9, pg 26
    Atom XMLListObject::_equals(Atom V) const
    {
        AvmCore *core = this->core();

        // null or "" return false
        if ((V == undefinedAtom) && (_length() == 0))
            return trueAtom;

        if (AvmCore::isXMLList(V))
        {
            XMLListObject *v = AvmCore::atomToXMLList(V);
            if (_length() != v->_length())
                return falseAtom;

            for (uint32_t i = 0; i < _length(); i++)
            {
                Atom a1 = m_children.get(i);
                Atom a2 = v->m_children.get(i);
                if ((a1 != a2) && core->equals (_getAt(i)->atom(), v->_getAt(i)->atom()) == falseAtom)
                    return falseAtom;
            }

            return trueAtom;
        }
        else if (_length() == 1)
        {
            return core->equals(_getAt(0)->atom(), V);
        }

        return falseAtom;
    }

    // E4X 9.2.1.10, pg 26
    Atom XMLListObject::_resolveValue()
    {
        if (_length() > 0)
            return this->atom();

        AvmCore *core = this->core();

        core->stackCheck(toplevel());

        fixTargetObject();
        if (AvmCore::isNull(m_targetObject) ||
            (m_targetProperty.isAttr()) ||
            (m_targetProperty.isAnyName()))
        {
            return nullObjectAtom;
        }

        Atom base = nullObjectAtom;
        XMLObject *x = AvmCore::atomToXMLObject(m_targetObject);
        if (x)
        {
            base = x->_resolveValue();
        }
        XMLListObject *xl = AvmCore::atomToXMLList(m_targetObject);
        if (xl)
        {
            base = xl->_resolveValue ();
        }
        if (AvmCore::isNull(base))
            return nullObjectAtom;

        XMLListObject *target = 0;
        if (AvmCore::isXML(base))
        {
            target = AvmCore::atomToXMLList(AvmCore::atomToXMLObject(base)->getMultinameProperty(m_targetProperty));
        }
        else if (AvmCore::isXMLList(base))
        {
            target = AvmCore::atomToXMLList(AvmCore::atomToXMLList(base)->getMultinameProperty(m_targetProperty));
        }
        else
        {
            AvmAssert(0);// base should be an XML or XMLList always
        }

        if (!target)
            return nullObjectAtom;

        if (!target->_length())
        {
            if (AvmCore::isXMLList(base) && AvmCore::atomToXMLList(base)->_length() > 1)
            {
                toplevel()->throwTypeError(kXMLAssigmentOneItemLists);
                return nullObjectAtom;
            }

            if (AvmCore::isXML(base))
            {
                AvmCore::atomToXMLObject(base)->setMultinameProperty(m_targetProperty, core->kEmptyString->atom());
                return AvmCore::atomToXMLObject(base)->getMultinameProperty(m_targetProperty);
            }
            else if (AvmCore::isXMLList(base))
            {
                AvmCore::atomToXMLList(base)->setMultinameProperty(m_targetProperty, core->kEmptyString->atom());
                return AvmCore::atomToXMLList(base)->getMultinameProperty(m_targetProperty);
            }
            else
            {
                AvmAssert(0); // b should be one or the other at this point since target is non-null
            }
        }

        return target->atom();
    }

    XMLObject *XMLListObject::_getAt (uint32_t i) const
    {
        if (i >= _length())
            return 0;

        Atom a = m_children.get(i);
        XMLObject* obj = AvmCore::atomToXMLObject(a);
        if (!obj)
        {
            obj = XMLObject::create(core()->GetGC(), toplevel()->xmlClass(), (E4XNode*) AvmCore::atomToGenericObject(a));
            m_children.set(i, obj->atom());
        }
        return obj;
    }

    E4XNode *XMLListObject::_getNodeAt (uint32_t i) const
    {
        if (i >= _length())
            return 0;

        Atom a = m_children.get(i);
        XMLObject* obj = AvmCore::atomToXMLObject(a);
        if (obj)
            return obj->getNode();
        return (E4XNode*) AvmCore::atomToGenericObject(a);
    }

    // E4X 12.2, page 59
    // Support for for-in, for-each for XMLListObjects
    Atom XMLListObject::nextName(int index)
    {
        AvmAssert(index > 0);

        if (index <= (int)_length())
        {
            AvmCore *core = this->core();
            return core->internInt (index-1)->atom();
        }
        else
        {
            return nullStringAtom;
        }
    }

    Atom XMLListObject::nextValue(int index)
    {
        AvmAssert(index > 0);

        if (index <= (int)_length())
            return _getAt (index-1)->atom();
        else
            return undefinedAtom;
    }

    int XMLListObject::nextNameIndex(int index)
    {
        AvmAssert(index >= 0);

        // XMLList types return the same number of indexes as children
        // We return a 1-N iterator (and then subtract off the one in
        // the nextName and nextValue functions).
        if (index < (int) _length())
            return index + 1;
        else
            return 0;
    }

    // E4X 13.5.4.21, page 93 which just refers to 10.2, page 31 (XMLList case)
    void XMLListObject::__toXMLString(PrintWriter &output, Atom, int /*indentLevel*/)
    {
        AvmCore *core = this->core();

        // For i = 0 to x.[[length]-1
        //  if (xml.prettyprinting == true and i is not equal to 0)
        //      s = s + line terminator
        //  s = s + toxmlstring(x[i], ancestor namespace)

        for (uint32_t i = 0; i < _length(); i++)
        {
            // iterate over entire array. If any prop is an "element" type, return false
            XMLObject *xm = _getAt(i);
            if (xm)
            {
                // !!@ This does not seem be affected by the prettyPrinting flag
                // but always occurs (comparing to Rhino)
                if (/*xmlClass()->getPrettyPrinting() && */i)
                    output << "\n";
                    NamespaceList AncestorNamespaces(core->GetGC(), kListInitialCapacity);
                    xm->__toXMLString(output, AncestorNamespaces, 0);
            }
        }

        return;
    }

    //////////////////////////////////////////////////////////////////////
    // E4X Section 13.5.4 below (AS methods)
    //////////////////////////////////////////////////////////////////////

    // E4X 13.5.4.2, pg 88
    XMLListObject *XMLListObject::AS3_attribute (Atom arg)
    {
        // name= ToAttributeName (attributeName);
        // return [[get]](name)

        return AvmCore::atomToXMLList(getAtomProperty(toplevel()->ToAttributeName(arg)->atom()));
    }

    // E4X 13.5.4.3, pg 88
    XMLListObject *XMLListObject::AS3_attributes ()
    {
        return AvmCore::atomToXMLList(getAtomProperty(toplevel()->ToAttributeName(core()->kAsterisk)->atom()));
    }

    // E4X 13.5.4.4, pg 88
    XMLListObject *XMLListObject::AS3_child (Atom propertyName)
    {
        AvmCore *core = this->core();

        XMLListObject *m = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), this->atom());

        for (uint32_t i = 0; i < _length(); i++)
        {
            XMLObject *x = _getAt(i);
            XMLListObject *rxl = x->child (propertyName);
            if (rxl && rxl->_length())
            {
                m->_append (rxl->atom());
            }
        }

        return m;
    }

    XMLListObject *XMLListObject::AS3_children ()
    {
        return AvmCore::atomToXMLList(getStringProperty(core()->kAsterisk));
    }

    XMLListObject *XMLListObject::AS3_comments ()
    {
        AvmCore *core = this->core();

        XMLListObject *m = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), this->atom());

        for (uint32_t i = 0; i < _length(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *x = _getAt(i);
                XMLListObject *rxl = x->comments();
                if (rxl && rxl->_length())
                {
                    m->_append (rxl->atom());
                }
            }
        }

        return m;
    }

    bool XMLListObject::AS3_contains (Atom value)
    {
        AvmCore *core = this->core();;
        for (uint32_t i = 0; i < _length(); i++)
        {
            // Spec says "comparison l[i] == value)" which is different than _equals
            if (core->equals(_getAt(i)->atom(), value) == trueAtom)
                return true;
        }

        return false;
    }

    XMLListObject *XMLListObject::AS3_copy ()
    {
        return _deepCopy();
    }

    // E4X 13.5.4.10, pg 90
    XMLListObject *XMLListObject::AS3_elements (Atom name) // name defaults to '*'
    {
        AvmCore *core = this->core();
        Toplevel* toplevel = this->toplevel();

        Multiname m;
        toplevel->ToXMLName (name, m);

        XMLListObject *xl = XMLListObject::create(core->GetGC(), toplevel->xmlListClass(), this->atom(), &m);

        for (uint32_t i = 0; i < _length(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *x = _getAt(i);
                XMLListObject *rxl = x->elements(name);
                if (rxl && rxl->_length())
                {
                    xl->_append (rxl->atom());
                }
            }
        }

        return xl;
    }

    // E4X 13.5.4.11, pg 90
    bool XMLListObject::XMLList_AS3_hasOwnProperty (Atom P)
    {
/*Bug : 3951226 : when XMLObject/XMLListObject's function are called with Object type variable from 
actionscript side we get Object type ATOM as this pointer. Object is always handled as Atom in AOT. 
therefore using this check to correctly finding out the object and call the function.*/
#ifdef VMCFG_HALFMOON_AOT_RUNTIME
        Atom thisAtom = (Atom)this;
        if(atomKind(thisAtom) == kObjectType)
        {
            ScriptObject* obj = AvmCore::atomToScriptObject(thisAtom);
            if(obj->hasAtomProperty(P))
                return true;
            
            return false;
        }
#endif
        if (hasAtomProperty(P))
            return true;

        // If x has a property with name ToString(P), return true;
        // !!@ (prototype different than regular object??

        return false;
    }

    // E4X 13.5.4.12, pg 90
    bool XMLListObject::AS3_hasComplexContent ()
    {
        if (_length() == 0)
            return false;

        if (_length() == 1)
        {
            return _getNodeAt(0)->hasComplexContent();
        }

        for (uint32_t i = 0; i < _length(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                return true;
            }
        }

        return false;
    }

    // E4X 13.5.4.13, pg 91
    bool XMLListObject::AS3_hasSimpleContent ()
    {
        if (!_length())
        {
            return true;
        }
        else if (_length() == 1)
        {
            return _getNodeAt(0)->hasSimpleContent();
        }
        else
        {
            for (uint32_t i = 0; i < _length(); i++)
            {
                if (_getNodeAt(i)->getClass() == E4XNode::kElement)
                {
                    return false;
                }
            }
        }

        return true;
    }

    uint32_t XMLListObject::AS3_length() const
    {
        return _length();
    }

    XMLListObject *XMLListObject::AS3_normalize ()
    {
        AvmCore *core = this->core();

        uint32_t i = 0;
        while (i < _length())
        {
            E4XNode* xn = _getNodeAt(i);
            if (xn->getClass() == E4XNode::kElement)
            {
                XMLObject *x = _getAt(i);
                x->normalize();
                i++;
            }
            else if ((xn->getClass() & (E4XNode::kText | E4XNode::kCDATA)))
            {
                while (((i + 1) < _length()) && ((_getNodeAt(i + 1)->getClass() & (E4XNode::kText | E4XNode::kCDATA))))
                {
                    xn->setValue (core->concatStrings(xn->getValue(), _getNodeAt(i + 1)->getValue()));
                    deleteAtomProperty(core->intToAtom(i + 1));
                }
                if (xn->getValue()->length() == 0)
                {
                    deleteAtomProperty(core->intToAtom(i));
                }
                else
                {
                    i++;
                }
            }
            else
            {
                i++;
            }
        }

        return this;
    }

    Atom XMLListObject::AS3_parent ()
    {
        if (!_length())
            return undefinedAtom;

        E4XNode *parent = _getNodeAt(0)->getParent();
        for (uint32_t i = 1; i < _length(); i++)
        {
            E4XNode *p = _getNodeAt(i)->getParent();
            if (parent != p)
                return undefinedAtom;
        }

        if (parent)
            return _getAt(0)->getParent()->atom();
        else
            return undefinedAtom;
    }

    XMLListObject *XMLListObject::AS3_processingInstructions (Atom name) // name defaults to '*'
    {
        AvmCore *core = this->core();
        XMLListObject *m = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), this->atom());

        for (uint32_t i = 0; i < _length(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *x = _getAt(i);
                XMLListObject *rxl = x->processingInstructions(name);
                if (rxl && rxl->_length())
                {
                    m->_append (rxl->atom());
                }
            }
        }

        return m;
    }

    bool XMLListObject::XMLList_AS3_propertyIsEnumerable(Atom P) // NOT virtual, NOT an override
    {
        double index = AvmCore::number(P);
        if ((index >= 0.0) && (index < _length()))
            return true;

        return false;
    }

    XMLListObject *XMLListObject::AS3_text ()
    {
        AvmCore *core = this->core();

        XMLListObject *m = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), this->atom());

        for (uint32_t i = 0; i < _length(); i++)
        {
            if (_getNodeAt(i)->getClass() == E4XNode::kElement)
            {
                XMLObject *x = _getAt(i);
                XMLListObject *rxl = x->text();
                if (rxl && rxl->_length())
                {
                    m->_append (rxl->atom());
                }
            }
        }

        return m;
    }

    // E4X 10.1.2, page 28
    Stringp XMLListObject::toString()
    {
        AvmCore *core = this->core();
        if (hasSimpleContent ())
        {
            Stringp output = core->kEmptyString;

            // s is the empty string
            // for all props in children array
            // if (class != comment or processing instruction)
            // s = s + toString(prop)
            for (uint32_t i = 0; i < _length(); i++)
            {
                E4XNode* xn = _getNodeAt(i);
                if ((xn->getClass() != E4XNode::kComment) && (xn->getClass() != E4XNode::kProcessingInstruction))
                {
                    XMLObject *xm = _getAt(i);
                    output = core->concatStrings(output, xm->toString());
                }
            }

            return output;
        }
        else
        {
            StringBuffer output(core);
            this->__toXMLString(output, nullStringAtom, 0);
            return core->newStringUTF8(output.c_str());
        }
    }

    Stringp XMLListObject::AS3_toString()
    {
        return toString();
    }

    String *XMLListObject::AS3_toXMLString ()
    {
        StringBuffer output(core());
        this->__toXMLString(output, nullStringAtom, 0);
        return core()->newStringUTF8(output.c_str());
    }

    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////
    // Routines below are not in the spec but work if XMLList has one element
    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////

    XMLObject *XMLListObject::AS3_addNamespace (Atom _namespace)
    {
        if (_length() == 1)
        {
            return _getAt(0)->addNamespace(_namespace);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("addNamespace"));
            return 0;
        }
    }

    XMLObject *XMLListObject::AS3_appendChild (Atom child)
    {
        if (_length() == 1)
        {
            return _getAt(0)->appendChild (child);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("appendChild"));
            return 0;
        }
    }

    int XMLListObject::AS3_childIndex ()
    {
        if (_length() == 1)
        {
            return _getNodeAt(0)->childIndex ();
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("childIndex"));
            return -1;
        }
    }

    ArrayObject *XMLListObject::AS3_inScopeNamespaces ()
    {
        if (_length() == 1)
        {
            return _getAt(0)->inScopeNamespaces();
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("inScopeNamespaces"));
            return 0;
        }
    }

    Atom XMLListObject::AS3_insertChildAfter (Atom child1, Atom child2)
    {
        if (_length() == 1)
        {
            return _getAt(0)->insertChildAfter (child1, child2);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("insertChildAfter"));
            return undefinedAtom;
        }
    }

    Atom XMLListObject::AS3_insertChildBefore (Atom child1, Atom child2)
    {
        if (_length() == 1)
        {
            return _getAt(0)->insertChildBefore (child1, child2);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("insertChildBefore"));
            return undefinedAtom;
        }
    }

    Atom XMLListObject::AS3_name()
    {
        if (_length() == 1)
        {
            return _getAt(0)->name();
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("name"));
            return nullStringAtom;
        }
    }


    Atom XMLListObject::_namespace (Atom prefix, int argc) // prefix is optional
    {
        AvmAssert(argc == 0 || argc == 1);

        if (_length() == 1)
        {
            return _getAt(0)->_namespace(prefix, argc);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("namespace"));
            return nullStringAtom;
        }
    }

    Atom XMLListObject::AS3_localName ()
    {
        if (_length() == 1)
        {
            return _getAt(0)->localName();
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("localName"));
            return nullStringAtom;
        }
    }

    ArrayObject *XMLListObject::AS3_namespaceDeclarations ()
    {
        if (_length() == 1)
        {
            return _getAt(0)->namespaceDeclarations();
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("namespaceDeclarations"));
            return 0;
        }
    }

    String *XMLListObject::AS3_nodeKind ()
    {
        // if our list has one element, return the nodeKind of the first element
        if (_length() == 1)
        {
            return _getNodeAt(0)->nodeKind(toplevel());
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("nodeKind"));
            return 0;
        }
    }

    XMLObject *XMLListObject::AS3_prependChild (Atom value)
    {
        if (_length() == 1)
        {
            return _getAt(0)->prependChild(value);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("prependChild"));
            return 0;
        }
    }

    XMLObject *XMLListObject::AS3_removeNamespace (Atom _namespace)
    {
        if (_length() == 1)
        {
            return _getAt(0)->removeNamespace (_namespace);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("removeNamespace"));
            return 0;
        }
    }

    XMLObject *XMLListObject::AS3_replace (Atom propertyName, Atom value)
    {
        if (_length() == 1)
        {
            return _getAt(0)->replace(propertyName, value);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("replace"));
            return 0;
        }
    }

    XMLObject *XMLListObject::AS3_setChildren (Atom value)
    {
        if (_length() == 1)
        {
            return _getAt(0)->setChildren (value);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("setChildren"));
            return 0;
        }
    }

    void XMLListObject::AS3_setLocalName (Atom name)
    {
        if (_length() == 1)
        {
            _getAt(0)->setLocalName (name);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("setLocalName"));
        }
    }

    void XMLListObject::AS3_setName (Atom name)
    {
        if (_length() == 1)
        {
            _getAt(0)->setName(name);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("setName"));
        }
    }

    void XMLListObject::AS3_setNamespace (Atom ns)
    {
        if (_length() == 1)
        {
            _getAt(0)->setNamespace (ns);
        }
        else
        {
            // throw type error
            toplevel()->throwTypeError(kXMLOnlyWorksWithOneItemLists, core()->toErrorString("setNamespace"));
        }
    }

#ifdef XML_FILTER_EXPERIMENT
    XMLListObject * XMLListObject::filter (Atom propertyName, Atom value)
    {
        Multiname m;
        toplevel()->ToXMLName(propertyName, m);

        Multiname name;
        toplevel()->CoerceE4XMultiname(&m, name);

        // filter opcode experiment
        XMLListObject *l = XMLListObject::create(core()->gc, toplevel()->xmlListClass());
        for (uint32_t i = 0; i < _length(); i++)
        {
            XMLObject *xm = _getAt(i);
            xm->_filter (l, name, value);
        }

        return l;
    }
#endif // XML_FILTER_EXPERIMENT

    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////

    // reserved for now
    void XMLListObject::setNotification(ScriptObject* /*f*/)
    {
        // nop
    }

    ScriptObject* XMLListObject::getNotification()
    {
        return 0;
    }

#ifdef DEBUGGER
    uint64_t XMLListObject::bytesUsedShallow() const
    {
        if (!m_created) {
            m_flushBeforeConstructionComplete = true;
            return 0;
        }

        uint64_t size = ScriptObject::bytesUsed();
        size += m_children.bytesUsed();

        return size;
    }

    uint64_t XMLListObject::bytesUsed() const
    {
        if (getSampler()->getSamplerType() == Sampler::SAMPLER_TYPE_AVMPLUS) {
            // To calculate the memory used by an XMLList, we add the memory taken
            // by each XML or E4XNode that we point to.
            //
            // FIXME As described in https://bugzilla.mozilla.org/show_bug.cgi?id=552307 ,
            // this implementation can cause the same memory to be accounted for more
            // than once in the profiler.  It is easy for the user to have multiple
            // XMLLists and XMLs that point into the same tree of E4XNodes.  For now,
            // there is not much we can do about this.  A possible fix is described in
            // https://bugzilla.mozilla.org/show_bug.cgi?id=558385

            uint64_t size = ScriptObject::bytesUsed();
            size += m_children.bytesUsed();

            // It's possible -- likely, in fact -- that several XML elements in an
            // XMLList will actually be pointers into the same tree of E4XNodes.
            // We don't want to count their memory twice, so this hashtable keeps
            // track of the root E4XNodes whose memory we have already accounted
            // for.
            HeapHashtable* seenXmlRoots = HeapHashtable::create(gc());

            for (uint32_t i=0, n=m_children.length(); i<n; ++i)
            {
                Atom child = m_children.get(i);

                // list members can be either XMLObjects or E4XNodes
                XMLObject* xmlobj = AvmCore::atomToXMLObject(child);
                E4XNode* node;
                if (xmlobj)
                    node = xmlobj->getNode();
                else
                    node = (E4XNode*)AvmCore::atomToGenericObject(child);

                if (node)
                {
                    // find the root node of the tree
                    E4XNode* root = node;
                    while (root->getParent())
                        root = root->getParent();

                    Atom atomizedRoot = AvmCore::genericObjectToAtom(root);

                    // Have we already seen that root node, as the root node of
                    // some other XML object in this list?
                    if (!seenXmlRoots->contains(atomizedRoot))
                    {
                        seenXmlRoots->add(atomizedRoot, trueAtom);

                        // Again, the XMLList might contain XMLObjects or it might
                        // contain E4XNodes
                        if (xmlobj)
                            size += xmlobj->bytesUsed();
                        else
                            size += node->bytesUsed();
                    }
                    else
                    {
                        // We have already accounted for the memory taken by the
                        // tree of E4XNodes, but we still need to take into
                        // account the small amount of memory taken by the XMLObject
                        // that points to that tree.
                        if (xmlobj)
                            size += xmlobj->bytesUsedShallow();
                    }
                }
            }

            delete seenXmlRoots;

            return size;
        } else {
            // For newer samplers, we report the bytes used only for this XMLListObject, not for 
            // the underlying E4XNodes. We will report the memory used for E4XNodes individually, 
            // eliminating problems with reporting the same memory more than once as described above.

            return bytesUsedShallow();
        }
    }

    void XMLListObject::registerDependentObjects(IMemorySampler* memSampler)
    {
        if (!m_created) {
            m_flushBeforeConstructionComplete = true;
            return;
        }

        // Add the children list as a dependent object.
        memSampler->addDependentObject(this, &m_children);

        // Call the base class.
        ScriptObject::registerDependentObjects(memSampler);
    }    
#endif
}
